{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Neural Networks from Scratch\n",
        "\n",
        "In this notebook we are going to implement and train a neural network from scratch using only numpy!\n",
        "\n",
        "\n",
        "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/SharifiZarchi/Introduction_to_Machine_Learning/blob/main/Jupyter_Notebooks/Chapter_03_Neural_Networks/NNs_from_scratch.ipynb)\n",
        "[![Open In kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://raw.githubusercontent.com/SharifiZarchi/Introduction_to_Machine_Learning/main/Jupyter_Notebooks/Chapter_03_Neural_Networks/NNs_from_scratch.ipynb)"
      ],
      "metadata": {
        "id": "Ecaqr9YkWpbL"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "PqFOwWLZVPI0",
        "cellView": "form"
      },
      "outputs": [],
      "source": [
        "# @title setup and imports\n",
        "\n",
        "import numpy as np\n",
        "from matplotlib import pyplot as plt\n",
        "from sklearn.datasets import fetch_openml\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.metrics import confusion_matrix\n",
        "from tqdm import trange"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# @title helper functions\n",
        "\n",
        "def plot_training(losses):\n",
        "    # Plot the loss\n",
        "    plt.plot(losses)\n",
        "    plt.title(\"Training loss\")\n",
        "    plt.xlabel(\"Epoch\")\n",
        "    plt.ylabel(\"Loss\")\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "def plot_confusion_matrix(y_true, y_pred, class_names, kept_classes):\n",
        "    dim = len(kept_classes)\n",
        "    labels = [class_names[i] for i in kept_classes]\n",
        "    # Plot the confusion matrix\n",
        "    conf_mat = confusion_matrix(y_true, y_pred)\n",
        "    norm_conf_mat = conf_mat / np.sum(conf_mat, axis=1)\n",
        "    # plot the matrix\n",
        "    fig, ax = plt.subplots()\n",
        "    plt.imshow(norm_conf_mat)\n",
        "    plt.title('Confusion Matrix')\n",
        "    plt.xlabel('Predictions')\n",
        "    plt.ylabel('Labels')\n",
        "    plt.xticks(range(dim), labels, rotation=45)\n",
        "    plt.yticks(range(dim), labels)\n",
        "    plt.colorbar()\n",
        "    # Put number of each cell in plot\n",
        "    for i in range(dim):\n",
        "        for j in range(dim):\n",
        "            c = conf_mat[j, i]\n",
        "            color = 'black' if c > 500 else 'white'\n",
        "            ax.text(i, j, str(int(c)), va='center', ha='center', color=color)\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "def get_data(filter_classes):\n",
        "    fashion_mnist = fetch_openml(\"Fashion-MNIST\", parser='auto')\n",
        "    x, y = fashion_mnist['data'], fashion_mnist['target'].astype(int)\n",
        "    # Remove classes\n",
        "    filtered_indices = np.isin(y, filter_classes)\n",
        "    x, y = x[filtered_indices].to_numpy(), y[filtered_indices]\n",
        "    # Normalize the pixels to be in [-1, +1] range\n",
        "    x = ((x / 255.) - .5) * 2\n",
        "    removed_class_count = 0\n",
        "    for i in range(10):  # Fix the labels\n",
        "        if i in filter_classes and removed_class_count != 0:\n",
        "            y[y == i] = i - removed_class_count\n",
        "        elif i not in filter_classes:\n",
        "            removed_class_count += 1\n",
        "    # Do the train-test split\n",
        "    return train_test_split(x, y, test_size=10_000)\n",
        "\n",
        "\n",
        "def onehot_encoder(y, num_labels):\n",
        "    one_hot = np.zeros(shape=(y.size, num_labels), dtype=int)\n",
        "    one_hot[np.arange(y.size), y] = 1\n",
        "    return one_hot"
      ],
      "metadata": {
        "id": "9AKM0RgiVSOQ",
        "cellView": "form"
      },
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Layers"
      ],
      "metadata": {
        "id": "GvFUeXMNadnZ"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Abstract Layer Class\n",
        "\n",
        "The `Layer` class serves as an abstract base class for all layers in the network. It includes placeholder methods:\n",
        "- `forward`: The forward pass computes the output of the layer given an input.\n",
        "- `backward`: The backward pass computes the gradients with respect to the input and parameters.\n",
        "- `step`: Updates the layer parameters (weights and biases)."
      ],
      "metadata": {
        "id": "KThKuK5DJHAH"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Layer:\n",
        "    def __init__(self):\n",
        "        self.inp = None\n",
        "        self.out = None\n",
        "\n",
        "    def __call__(self, inp: np.ndarray) -> np.ndarray:\n",
        "        return self.forward(inp)\n",
        "\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        raise NotImplementedError\n",
        "\n",
        "    def backward(self, up_grad: np.ndarray) -> np.ndarray:\n",
        "        raise NotImplementedError\n",
        "\n",
        "    def step(self, lr: float) -> None:\n",
        "        pass"
      ],
      "metadata": {
        "id": "5HmznEXldEaI"
      },
      "execution_count": 3,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Linear Layers\n",
        "\n",
        "The `Linear` class implements the fully connected (or dense) layer of a neural network, which performs a linear transformation on the input:\n",
        "\n",
        "$$\\mathbf{y} = \\mathbf{x} \\cdot \\mathbf{W} + \\mathbf{b}$$"
      ],
      "metadata": {
        "id": "eB1SjTeaJztY"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Initialization**\n",
        "- `self.w`: Represents the weight matrix of shape `(in_dim, out_dim)`, initialized using small random values.\n",
        "- `self.b`: Bias vector of shape `(1, out_dim)`, initialized to zeros.\n",
        "- `self.dw` and `self.db`: These store the computed gradients of weights and biases during backpropagation.\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "**Forward Pass**\n",
        "- The forward pass computes:\n",
        "$$\\mathbf{out} = \\mathbf{inp} \\cdot \\mathbf{W} + \\mathbf{b}$$\n",
        "where:\n",
        "  - `inp`: Input matrix of shape `(batch_size, in_dim)`\n",
        "  - `self.w`: Weight matrix of shape `(in_dim, out_dim)`\n",
        "  -\t`self.b`: Bias matrix of shape `(1, out_dim)`\n",
        "-\tThe result is a matrix out of shape `(batch_size, out_dim)`.\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "**Backward Pass**\n",
        "- The backward pass computes gradients needed for updating the weights and biases. Given the upstream gradient `up_grad` (from the loss with respect to the output of this layer), we calculate:\n",
        "  - Gradient w.r.t. weights (`self.dw`):\n",
        "    $$ \\frac{\\partial L}{\\partial W} = \\mathbf{inp}^T \\cdot \\text{up_grad} $$\n",
        "  - Gradient w.r.t. biases (`self.db`):\n",
        "    $$\\frac{\\partial L}{\\partial b} = \\sum \\text{up_grad} \\text{ (summed across batch)}$$\n",
        "  - Gradient to propagate to the previous layer (`down_grad`):\n",
        "    $$\\text{down_grad} = \\text{up_grad} \\cdot W^T$$\n",
        "- This allows the gradient to flow backward to earlier layers.\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "**Step Method**\n",
        "- Updates the weights and biases using the computed gradients and learning rate (`lr`):\n",
        "    $$W = W - lr \\cdot \\frac{\\partial L}{\\partial W}$$\n",
        "    $$b = b - lr \\cdot \\frac{\\partial L}{\\partial b}$$\n"
      ],
      "metadata": {
        "id": "phTfqRsVIZKA"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Linear(Layer):\n",
        "    def __init__(self, in_dim: int, out_dim: int):\n",
        "        super().__init__()\n",
        "        # He initialization: better scaling for deep networks\n",
        "        self.w = 0.1 * np.random.randn(in_dim, out_dim)\n",
        "        self.b = np.zeros((1, out_dim))\n",
        "        self.dw = np.zeros_like(self.w)\n",
        "        self.db = np.zeros_like(self.b)\n",
        "\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Perform the linear transformation: output = inp * W + b\"\"\"\n",
        "        self.inp = inp\n",
        "        self.out = np.dot(inp, self.w) + self.b\n",
        "        return self.out\n",
        "\n",
        "    def backward(self, up_grad: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Backpropagate the gradients through this layer.\"\"\"\n",
        "        # Compute gradients for weights and biases\n",
        "        self.dw = np.dot(self.inp.T, up_grad)  # Gradient wrt weights\n",
        "        self.db = np.sum(up_grad, axis=0, keepdims=True)  # Gradient wrt biases\n",
        "        # Compute gradient to propagate back (downstream)\n",
        "        down_grad = np.dot(up_grad, self.w.T)\n",
        "        return down_grad\n",
        "\n",
        "    def step(self, lr: float) -> None:\n",
        "        \"\"\"Update the weights and biases using the gradients.\"\"\"\n",
        "        self.w -= lr * self.dw\n",
        "        self.b -= lr * self.db"
      ],
      "metadata": {
        "id": "pF0DKJV_JRnh"
      },
      "execution_count": 4,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Activation Functions\n",
        "\n",
        "We can implement activation functions as layers. This will simplify the training process"
      ],
      "metadata": {
        "id": "yr6hBcY4N00B"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Sigmoid\n",
        "\n",
        "- The Sigmoid function is defined as follows:\n",
        "\n",
        "$$f(x) = \\frac{1}{1 + e^{-x}}$$\n",
        "\n",
        "- Sigmoid squashes the input into the range [0, 1], making it useful for binary classification tasks.\n",
        "- It converts any real-valued number into a probability-like output.\n",
        "- However, in deeper networks, it may cause vanishing gradients due to its flat slope for extreme values.\n",
        "- The derivative of Sigmoid is convenient to compute using its output  $f(x)$:\n",
        "$$f'(x) = \\frac{-e^{-x}}{(1 + e^{-x})^2} = \\frac{1}{1 + e^{-x}} \\cdot \\frac{e^{-x}}{1 + e^{-x}} = f(x) \\cdot (1-f(x))$$"
      ],
      "metadata": {
        "id": "zsuMui_fLCWf"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Sigmoid(Layer):\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Sigmoid Activation: f(x) = 1 / (1 + exp(-x))\"\"\"\n",
        "        self.out = 1 / (1 + np.exp(-inp))\n",
        "        return self.out\n",
        "\n",
        "    def backward(self, up_grad: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Backward pass for Sigmoid: f'(x) = f(x) * (1 - f(x))\"\"\"\n",
        "        down_grad = self.out * (1 - self.out) * up_grad\n",
        "        return down_grad"
      ],
      "metadata": {
        "id": "cxGzqMybEGt-"
      },
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### ReLU (Rectified Linear Unit)\n",
        "\n",
        "- The ReLU function outputs 0 if the input is less than zero, otherwise it will return the input itself:\n",
        "$$f(x) = \\max(0,x) $$\n",
        "\n",
        "- ReLU helps introduce non-linearity into the model, which is essential for learning complex patterns.\n",
        "- It also helps avoid the vanishing gradient problem common in deep networks with the Sigmoid activation.\n",
        "- During backpropagation, only the gradients for inputs greater than 0 pass through:\n",
        "\n",
        "$$ f'(x) = \\begin{cases} 1 & \\text{if } x > 0 \\\\ 0 & \\text{otherwise} \\end{cases}$$\n"
      ],
      "metadata": {
        "id": "08aibrACJ_kr"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class ReLU(Layer):\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"ReLU Activation: f(x) = max(0, x)\"\"\"\n",
        "        self.inp = inp\n",
        "        self.out = np.maximum(0, inp)\n",
        "        return self.out\n",
        "\n",
        "    def backward(self, up_grad: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Backward pass for ReLU: derivative is 1 where input > 0, else 0.\"\"\"\n",
        "        down_grad = up_grad * (self.inp > 0)  # Efficient boolean indexing\n",
        "        return down_grad"
      ],
      "metadata": {
        "id": "dHaTiE10HART"
      },
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Softmax\n",
        "\n",
        "- The Softmax function is defined as follows:\n",
        "$$f(x_i) = \\frac{e^{x_i}}{\\sum_j e^{x_j}}$$\n",
        "\n",
        "- Softmax normalizes the input values into probabilities that sum to 1.\n",
        "- It's typically used in the final layer of a neural network for multi-class classification.\n",
        "- It converts raw scores into probabilities, where each class has a non-negative probability between 0 and 1.\n",
        "- Subtracting the maximum input value (`np.max(inp)`) from all inputs before applying `np.exp` helps prevent overflow errors."
      ],
      "metadata": {
        "id": "CaQZUpp1coQ1"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Softmax(Layer):\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Softmax Activation: f(x) = exp(x) / sum(exp(x))\"\"\"\n",
        "        # Subtract max for numerical stability\n",
        "        exp_values = np.exp(inp - np.max(inp, axis=1, keepdims=True))\n",
        "        self.out = exp_values / np.sum(exp_values, axis=1, keepdims=True)\n",
        "        return self.out\n",
        "\n",
        "    def backward(self, up_grad: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Backward pass for Softmax using the Jacobian matrix.\"\"\"\n",
        "        down_grad = np.empty_like(up_grad)\n",
        "        for i in range(up_grad.shape[0]):\n",
        "            single_output = self.out[i].reshape(-1, 1)\n",
        "            jacobian = np.diagflat(single_output) - np.dot(single_output, single_output.T)\n",
        "            down_grad[i] = np.dot(jacobian, up_grad[i])\n",
        "        return down_grad"
      ],
      "metadata": {
        "id": "wrq1L5ZCO0vM"
      },
      "execution_count": 7,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Loss Functions"
      ],
      "metadata": {
        "id": "sl2nW4KnSgYs"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Abstract Loss Class\n",
        "\n",
        "The `Loss` class serves as an abstract base class for all layers in the network. It includes placeholder methods:\n",
        "- `forward`: To compute the loss given predictions and targets.\n",
        "- `backward`: To compute the loss given predictions and targets."
      ],
      "metadata": {
        "id": "oyEmTPR-TLDk"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class Loss:\n",
        "    def __init__(self):\n",
        "        self.prediction = None\n",
        "        self.target = None\n",
        "        self.loss = None\n",
        "\n",
        "    def __call__(self, prediction: np.ndarray, target: np.ndarray) -> float:\n",
        "        return self.forward(prediction, target)\n",
        "\n",
        "    def forward(self, prediction: np.ndarray, target: np.ndarray) -> float:\n",
        "        raise NotImplementedError\n",
        "\n",
        "    def backward(self) -> np.ndarray:\n",
        "        raise NotImplementedError"
      ],
      "metadata": {
        "id": "qMb2DsJNSp3f"
      },
      "execution_count": 8,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Cross-Entropy Loss\n",
        "\n",
        "Cross-entropy loss is typically used in classification tasks since it measures the dissimilarity between the true distribution (target) and the predicted probability distribution (prediction):\n",
        "\n",
        "$$L = - \\frac{1}{N} \\sum_{i} \\sum_{c} y_{ic} \\log(p_{ic})$$\n",
        "\n",
        "where $y_{ic}$ is the one-hot encoded true label (target), $p_{ic}$ is the predicted probability (output from Softmax) and $N$ is the batch size.\n"
      ],
      "metadata": {
        "id": "__nX-zlJTo3B"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class CrossEntropy(Loss):\n",
        "    def forward(self, prediction: np.ndarray, target: np.ndarray) -> float:\n",
        "        \"\"\"Cross-Entropy Loss for classification.\"\"\"\n",
        "        self.prediction = prediction\n",
        "        self.target = target\n",
        "        # Clip predictions to avoid log(0)\n",
        "        clipped_pred = np.clip(prediction, 1e-12, 1.0)\n",
        "        # Compute and return the loss\n",
        "        self.loss = -np.mean(np.sum(target * np.log(clipped_pred), axis=1))\n",
        "        return self.loss\n",
        "\n",
        "    def backward(self) -> np.ndarray:\n",
        "        \"\"\"Gradient of Cross-Entropy Loss.\"\"\"\n",
        "        # Gradient wrt prediction (assuming softmax and one-hot targets)\n",
        "        grad = -self.target / self.prediction / self.target.shape[0]\n",
        "        return grad"
      ],
      "metadata": {
        "id": "lyF-rhVvSy1q"
      },
      "execution_count": 9,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Mean Squared Error (MSE) Loss\n",
        "\n",
        "MSE is used primarily for regression tasks, where you need to measure the distance between the predicted continuous values and the true values:\n",
        "\n",
        "$$L = \\frac{1}{N} \\sum_{i} (p_i - y_i)^2$$\n",
        "\n",
        "where $p_i$ is the predicted value, $y_i$ is the true value (target) and $N$ is the batch size.\n",
        "\n",
        "The gradient measures the difference between the prediction and the target, scaled by the batch size:\n",
        "\n",
        "$$\\frac{\\partial L}{\\partial p_i} = \\frac{2}{N} (p_i - y_i)$$\n"
      ],
      "metadata": {
        "id": "-i8pHfR9TrYa"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class MSE(Loss):\n",
        "    def forward(self, prediction: np.ndarray, target: np.ndarray) -> float:\n",
        "        \"\"\"Mean Squared Error Loss for regression.\"\"\"\n",
        "        self.prediction = prediction\n",
        "        self.target = target\n",
        "        # Compute and return the loss\n",
        "        self.loss = np.mean((prediction - target) ** 2)\n",
        "        return self.loss\n",
        "\n",
        "    def backward(self) -> np.ndarray:\n",
        "        \"\"\"Gradient of MSE Loss.\"\"\"\n",
        "        grad = 2 * (self.prediction - self.target) / self.target.size\n",
        "        return grad"
      ],
      "metadata": {
        "id": "0ZmkwcRCSzRd"
      },
      "execution_count": 10,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Neural Network"
      ],
      "metadata": {
        "id": "p1KKCeDWUNJc"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now we can combine everything we've done earlier to build a neural network class called `MLP` with the following methods:\n",
        "\n",
        "- `forward`: Sequentially passes input through each layer in the network to compute the output.\n",
        "- `loss`: Computes the loss between the predicted output and the true target using the specified loss function.\n",
        "- `backward`: Propagates the gradient from the loss function through each layer, updating the gradients of the parameters in each layer.\n",
        "- `update`: Updates each layer's parameters (e.g., weights and biases) using the gradients computed during backpropagation.\n",
        "- `train`: Executes the training loop for a specified number of epochs, iterating over the dataset in mini-batches, performing the forward pass, computing the loss, backpropagating the gradients, and updating the parameters.\n"
      ],
      "metadata": {
        "id": "MJno_CD7hK1u"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class MLP:\n",
        "    def __init__(self, layers: list[Layer], loss_fn: Loss, lr: float) -> None:\n",
        "        \"\"\"\n",
        "        Multi-Layer Perceptron (MLP) class.\n",
        "        Arguments:\n",
        "        - layers: List of layers (e.g., Linear, ReLU, etc.).\n",
        "        - loss_fn: Loss function object (e.g., CrossEntropy, MSE).\n",
        "        - lr: Learning rate.\n",
        "        \"\"\"\n",
        "        self.layers = layers\n",
        "        self.loss_fn = loss_fn\n",
        "        self.lr = lr\n",
        "\n",
        "    def __call__(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Makes the model callable, equivalent to forward pass.\"\"\"\n",
        "        return self.forward(inp)\n",
        "\n",
        "    def forward(self, inp: np.ndarray) -> np.ndarray:\n",
        "        \"\"\"Pass input through each layer sequentially.\"\"\"\n",
        "        for layer in self.layers:\n",
        "            inp = layer.forward(inp)\n",
        "        return inp\n",
        "\n",
        "    def loss(self, prediction: np.ndarray, target: np.ndarray) -> float:\n",
        "        \"\"\"Calculate the loss.\"\"\"\n",
        "        return self.loss_fn(prediction, target)\n",
        "\n",
        "    def backward(self) -> None:\n",
        "        \"\"\"Perform backpropagation by propagating the gradient backwards through the layers.\"\"\"\n",
        "        up_grad = self.loss_fn.backward()\n",
        "        for layer in reversed(self.layers):\n",
        "            up_grad = layer.backward(up_grad)\n",
        "\n",
        "    def update(self) -> None:\n",
        "        \"\"\"Update the parameters of each layer using the gradients and the learning rate.\"\"\"\n",
        "        for layer in self.layers:\n",
        "            layer.step(self.lr)\n",
        "\n",
        "    def train(self, x_train: np.ndarray, y_train: np.ndarray, epochs: int, batch_size: int) -> np.ndarray:\n",
        "        \"\"\"Train the MLP over the given dataset for a number of epochs.\"\"\"\n",
        "        losses = np.empty(epochs)\n",
        "        for epoch in (pbar := trange(epochs)):\n",
        "            running_loss = 0.0\n",
        "            for i in range(0, len(x_train), batch_size):\n",
        "                x_batch = x_train[i:i + batch_size]\n",
        "                y_batch = y_train[i:i + batch_size]\n",
        "\n",
        "                # Forward pass\n",
        "                prediction = self.forward(x_batch)\n",
        "\n",
        "                # Compute loss\n",
        "                running_loss += self.loss(prediction, y_batch) * batch_size\n",
        "\n",
        "                # Backward pass\n",
        "                self.backward()\n",
        "\n",
        "                # Update parameters\n",
        "                self.update()\n",
        "\n",
        "            # Normalize running loss by total number of samples\n",
        "            running_loss /= len(x_train)\n",
        "            pbar.set_description(f\"Loss: {running_loss:.3f}\")\n",
        "            losses[epoch] = running_loss\n",
        "\n",
        "        return losses"
      ],
      "metadata": {
        "id": "nd1HjMLMUOAs"
      },
      "execution_count": 11,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Training"
      ],
      "metadata": {
        "id": "AzONLtl5kc8W"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Loading the Fashion-MNIST Dataset"
      ],
      "metadata": {
        "id": "r93pdZdrZ77B"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "For simplicity you can use `get_data` to load the Fashion-MNIST dataset. Since we aren't using GPUs, in order to save time and get better results, we are only going to include 3 classes in our training. However you can easily modify this cell to include different classes."
      ],
      "metadata": {
        "id": "bOCW3Kh9azY6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class_names = {0: 'T-shirt/top', 1: 'Trouser', 2: 'Pullover',\n",
        "               3: 'Dress', 4: 'Coat', 5:  'Sandal', 6: 'Shirt',\n",
        "               7: 'Sneaker', 8: 'Bag', 9: 'Ankle boot'}\n",
        "\n",
        "# Include all the classes you want to see in training\n",
        "kept_classes = [0, 1, 7]  # T-shirt/top, Trouser, Sneaker\n",
        "\n",
        "# Download the dataset and split it into training and testing sets\n",
        "x_train, x_test, y_train, y_test = get_data(kept_classes)\n",
        "\n",
        "# One-hot encode the target labels of the training set\n",
        "y_train = onehot_encoder(y_train, num_labels=len(kept_classes))"
      ],
      "metadata": {
        "id": "xmRyKM-oZ-hh"
      },
      "execution_count": 12,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Training the Network"
      ],
      "metadata": {
        "id": "sZeNgQXybi4P"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now we can define the network and train it on the dataset."
      ],
      "metadata": {
        "id": "2qfmP-hYkpfz"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Define the layers of the neural network\n",
        "layers = [Linear(784, 50),\n",
        "          ReLU(),\n",
        "          Linear(50, 50),\n",
        "          ReLU(),\n",
        "          Linear(50, len(kept_classes)),\n",
        "          Softmax()]\n",
        "\n",
        "# Create the model\n",
        "model = MLP(layers, CrossEntropy(), lr=0.001)\n",
        "\n",
        "# Train the model\n",
        "losses = model.train(x_train, y_train, epochs=30, batch_size=64)\n",
        "\n",
        "# Plot the training loss curve\n",
        "plot_training(losses)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 489
        },
        "id": "eaIADoc8W7CS",
        "outputId": "b60dd86e-6db5-4c33-9285-dec4f53aaf28"
      },
      "execution_count": 13,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "Loss: 0.058: 100%|██████████| 30/30 [00:32<00:00,  1.08s/it]\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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SZN07bXXymKx6Nlnnw2L3UWt7rzzTr3//vsaNWqUpkyZ4g8ogwcP1vLlyw/6/caPH69TTjlFt912mzZv3qyCggK9//77ev3113X99df7G6FnzpypTz/9VGeccYby8vJUVlamf/zjH+rTp49OOOEESdJpp52mrKwsjRo1SpmZmVq9erUeeeQRnXHGGfv0/AA4OIQcAIekZ8+e+u9//6sbb7xRt99+u9LS0nTJJZdozJgxGjt2rNnlSZKGDRumd955RzfddJPuuOMO5ebmaubMmVq9enWnjv76KavVqjfeeEPTp0/Xiy++qCeffFL5+fn6y1/+ohtvvNG/3JlnnqnNmzdr3rx5Ki8vV3p6uk466STdddddSklJkSRdeeWVeu655zR79mzV1taqT58+uvbaa3X77bcH7PsD0YprVwGIWmeffbZWrVqldevWmV0KgCCgJwdAVGhoaGj3fN26dXr77bd18sknm1MQgKBjJgdAVMjOztZll12mfv36acuWLZozZ44aGxu1bNkyHX744WaXByAI6MkBEBXGjRun559/XqWlpXI6nSoqKtK9995LwAEiGDM5AAAgItGTAwAAIhIhBwAARKSo68nxer0qKSlRUlKSLBaL2eUAAIBOMAxDNTU1ysnJkdXauTmaqAs5JSUlys3NNbsMAADQBdu2bVOfPn06tWzUhZzW06Rv27ZNycnJJlcDAAA6o7q6Wrm5uQd1uZOoCzmtu6iSk5MJOQAAdDMH02pC4zEAAIhIhBwAABCRCDkAACAiEXIAAEBEIuQAAICIRMgBAAARiZADAAAiEiEHAABEJEIOAACISIQcAAAQkQg5AAAgIhFyAABARCLkBIjHa6is2qUtFXVmlwIAAETICZivNlZo5L3z9bunF5tdCgAAECEnYNITnZKk8tpGkysBAAASISdgeiX5Qs6e+ia5m70mVwMAAAg5AZIaFyO71SJJqqhjNgcAALMRcgLEarW07bKqcZtcDQAAIOQEUHqSQ5K0q9ZlciUAAICQE0C9WmZydtWwuwoAALMRcgKotfmYkAMAgPkIOQHUGnLKa+nJAQDAbIScAEpndxUAAGGDkBNA7K4CACB8EHICyN94zFmPAQAwHSEngPw9OczkAABgOkJOAKW3hJyaxmY1uD0mVwMAQHQj5ARQktMup923SblQJwAA5iLkBJDFYvHvsipjlxUA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AABARCLkAACAiETICZBHH31U+fn5io2NVWFhoRYtWmR2SWHtzjvvlMViaXcbNGiQ2WWFnU8//VTjx49XTk6OLBaLXnvttXavG4ah6dOnKzs7W3FxcSouLta6devMKTaMHGi7XXbZZfv8/saNG2dOsWFi1qxZGjFihJKSkpSRkaGzzz5ba9eubbeMy+XS1KlT1bNnTyUmJuq8887Tzp07Tao4PHRmu5188sn7/N6uuuoqkyoOD3PmzNGQIUP8J/0rKirSO++84389UL81Qk4AvPjii5o2bZpmzJihpUuXqqCgQGPHjlVZWZnZpYW1o48+Wjt27PDfPv/8c7NLCjt1dXUqKCjQo48+2uHr999/v/7+979r7ty5+vrrr5WQkKCxY8fK5XKFuNLwcqDtJknjxo1r9/t7/vnnQ1hh+Pnkk080depUffXVV/rggw/U1NSk0047TXV1df5lbrjhBr355pt66aWX9Mknn6ikpETnnnuuiVWbrzPbTZIuv/zydr+3+++/36SKw0OfPn103333acmSJVq8eLF++ctf6qyzztKqVaskBfC3ZuCQjRw50pg6dar/ucfjMXJycoxZs2aZWFV4mzFjhlFQUGB2Gd2KJOPVV1/1P/d6vUZWVpbxl7/8xT9WWVlpOJ1O4/nnnzehwvD00+1mGIYxadIk46yzzjKlnu6irKzMkGR88sknhmH4flsxMTHGSy+95F9m9erVhiRj4cKFZpUZdn663QzDME466STjuuuuM6+obiItLc3417/+FdDfGjM5h8jtdmvJkiUqLi72j1mtVhUXF2vhwoUmVhb+1q1bp5ycHPXr108XX3yxtm7danZJ3cqmTZtUWlra7reXkpKiwsJCfnudsGDBAmVkZGjgwIGaMmWKKioqzC4prFRVVUmSevToIUlasmSJmpqa2v3eBg0apL59+/J728tPt1ur5557Tunp6Ro8eLBuvfVW1dfXm1FeWPJ4PHrhhRdUV1enoqKigP7Wou4CnYFWXl4uj8ejzMzMduOZmZlas2aNSVWFv8LCQj311FMaOHCgduzYobvuukujR4/Wd999p6SkJLPL6xZKS0slqcPfXutr6Ni4ceN07rnn6rDDDtOGDRv0xz/+UaeffroWLlwom81mdnmm83q9uv766zVq1CgNHjxYku/35nA4lJqa2m5Zfm9tOtpuknTRRRcpLy9P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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Evaluation"
      ],
      "metadata": {
        "id": "H4Uh-ddyk2mR"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "We can measure the models accuracy on the test dataset."
      ],
      "metadata": {
        "id": "sgO7eZ9Uk5X0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Test the model\n",
        "y_prediction = np.argmax(model(x_test), axis=1)\n",
        "acc = 100 * np.mean(y_prediction == y_test)\n",
        "print(f'Test accuracy with {len(y_train)} training examples on {len(y_test)} test samples is {acc:.2f}%')"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "I3M4fWNaZoHW",
        "outputId": "78f02c57-33b2-48ea-a9ea-288a1fbd4930"
      },
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Test accuracy with 11000 training examples on 10000 test samples is 98.46%\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "The confusion matrix can also be observed:"
      ],
      "metadata": {
        "id": "I2DrYQPilBlF"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Plot the confusion matrix\n",
        "plot_confusion_matrix(y_test, y_prediction, class_names, kept_classes)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 518
        },
        "id": "49LwgNLMj72R",
        "outputId": "6485c23a-8783-479d-e8ea-26b72f033525"
      },
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 2 Axes>"
            ],
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjQAAAH1CAYAAADh12SPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABnz0lEQVR4nO3ddVhU6dsH8O8ZUoEBBClFEOxYRV3btbC76xXE7sDuWnPtNbBx7V3XDhRRTNbG7mRVwiBEqZnz/sGP0VlQiZHhDN/PdZ1rnTPnPOeeYWHuuZ84giiKIoiIiIgkTKbtAIiIiIiyigkNERERSR4TGiIiIpI8JjREREQkeUxoiIiISPKY0BAREZHkMaEhIiIiyWNCQ0RERJLHhIaIiIgkjwkNEQEAHj58iIYNG8Lc3ByCIGDv3r0abf/Zs2cQBAG+vr4abVfK6tSpgzp16mg7DCKdwISGKAd5/Pgx+vXrBxcXFxgbG0Mul6NGjRpYunQpPn369EOv7enpiZs3b2LWrFnYvHkzKlWq9EOvl5169OgBQRAgl8vTfB8fPnwIQRAgCAIWLFiQ4fZfvXqFadOmITg4WAPRElFm6Gs7ACJKdujQIXTo0AFGRkbw8PBAmTJlkJCQgLNnz2L06NG4ffs21qxZ80Ou/enTJwQFBWHixIkYPHjwD7mGk5MTPn36BAMDgx/S/vfo6+vj48ePOHDgADp27Kj23NatW2FsbIy4uLhMtf3q1StMnz4dzs7OKF++fLrPO3bsWKauR0SpMaEhygGePn2Kzp07w8nJCSdOnIC9vb3quUGDBuHRo0c4dOjQD7t+REQEAMDCwuKHXUMQBBgbG/+w9r/HyMgINWrUwPbt21MlNNu2bUOzZs3w999/Z0ssHz9+RN68eWFoaJgt1yPKDdjlRJQDzJ8/Hx8+fMD69evVkpkURYoUwbBhw1SPk5KSMHPmTLi6usLIyAjOzs6YMGEC4uPj1c5zdnZG8+bNcfbsWVSuXBnGxsZwcXHBH3/8oTpm2rRpcHJyAgCMHj0agiDA2dkZQHJXTcq/vzRt2jQIgqC2z9/fHzVr1oSFhQVMTU1RvHhxTJgwQfX818bQnDhxArVq1YKJiQksLCzQqlUr3L17N83rPXr0CD169ICFhQXMzc3h5eWFjx8/fv2N/Y+uXbviyJEjiIyMVO27dOkSHj58iK5du6Y6/t27dxg1ahTKli0LU1NTyOVyNGnSBNevX1cdExgYiJ9//hkA4OXlpeq6SnmdderUQZkyZXDlyhX88ssvyJs3r+p9+e8YGk9PTxgbG6d6/Y0aNYKlpSVevXqV7tdKlNswoSHKAQ4cOAAXFxdUr149Xcf37t0bU6ZMQYUKFbB48WLUrl0bc+bMQefOnVMd++jRI7Rv3x4NGjTAwoULYWlpiR49euD27dsAgLZt22Lx4sUAgC5dumDz5s1YsmRJhuK/ffs2mjdvjvj4eMyYMQMLFy5Ey5Ytce7cuW+ed/z4cTRq1Ajh4eGYNm0avL29cf78edSoUQPPnj1LdXzHjh0RExODOXPmoGPHjvD19cX06dPTHWfbtm0hCAJ2796t2rdt2zaUKFECFSpUSHX8kydPsHfvXjRv3hyLFi3C6NGjcfPmTdSuXVuVXJQsWRIzZswAAPTt2xebN2/G5s2b8csvv6jaefv2LZo0aYLy5ctjyZIlqFu3bprxLV26FPnz54enpycUCgUAYPXq1Th27Bh+//13ODg4pPu1EuU6IhFpVVRUlAhAbNWqVbqODw4OFgGIvXv3Vts/atQoEYB44sQJ1T4nJycRgHj69GnVvvDwcNHIyEgcOXKkat/Tp09FAOJvv/2m1qanp6fo5OSUKoapU6eKX/75WLx4sQhAjIiI+GrcKdfYuHGjal/58uVFGxsb8e3bt6p9169fF2Uymejh4ZHqej179lRrs02bNqKVldVXr/nl6zAxMRFFURTbt28v1q9fXxRFUVQoFKKdnZ04ffr0NN+DuLg4UaFQpHodRkZG4owZM1T7Ll26lOq1pahdu7YIQPTx8Unzudq1a6vtO3r0qAhA/PXXX8UnT56IpqamYuvWrb/7GolyO1ZoiLQsOjoaAGBmZpau4w8fPgwA8Pb2Vts/cuRIAEg11qZUqVKoVauW6nH+/PlRvHhxPHnyJNMx/1fK2Jt9+/ZBqVSm65zXr18jODgYPXr0QL58+VT7f/rpJzRo0ED1Or/Uv39/tce1atXC27dvVe9henTt2hWBgYEIDQ3FiRMnEBoammZ3E5A87kYmS/4zqVAo8PbtW1V32tWrV9N9TSMjI3h5eaXr2IYNG6Jfv36YMWMG2rZtC2NjY6xevTrd1yLKrZjQEGmZXC4HAMTExKTr+OfPn0Mmk6FIkSJq++3s7GBhYYHnz5+r7S9UqFCqNiwtLfH+/ftMRpxap06dUKNGDfTu3Ru2trbo3Lkz/vzzz28mNylxFi9ePNVzJUuWxJs3bxAbG6u2/7+vxdLSEgAy9FqaNm0KMzMz7Ny5E1u3bsXPP/+c6r1MoVQqsXjxYhQtWhRGRkawtrZG/vz5cePGDURFRaX7mgUKFMjQAOAFCxYgX758CA4OxrJly2BjY5Puc4lyKyY0RFoml8vh4OCAW7duZei8/w7K/Ro9Pb0094uimOlrpIzvSJEnTx6cPn0ax48fR/fu3XHjxg106tQJDRo0SHVsVmTltaQwMjJC27ZtsWnTJuzZs+er1RkAmD17Nry9vfHLL79gy5YtOHr0KPz9/VG6dOl0V6KA5PcnI65du4bw8HAAwM2bNzN0LlFuxYSGKAdo3rw5Hj9+jKCgoO8e6+TkBKVSiYcPH6rtDwsLQ2RkpGrGkiZYWlqqzQhK8d8qEADIZDLUr18fixYtwp07dzBr1iycOHECJ0+eTLPtlDjv37+f6rl79+7B2toaJiYmWXsBX9G1a1dcu3YNMTExaQ6kTrFr1y7UrVsX69evR+fOndGwYUO4u7unek/Sm1ymR2xsLLy8vFCqVCn07dsX8+fPx6VLlzTWPpGuYkJDlAOMGTMGJiYm6N27N8LCwlI9//jxYyxduhRAcpcJgFQzkRYtWgQAaNasmcbicnV1RVRUFG7cuKHa9/r1a+zZs0ftuHfv3qU6N2WBuf9OJU9hb2+P8uXLY9OmTWoJwq1bt3Ds2DHV6/wR6tati5kzZ2L58uWws7P76nF6enqpqj9//fUXXr58qbYvJfFKK/nLqLFjx+LFixfYtGkTFi1aBGdnZ3h6en71fSSiZFxYjygHcHV1xbZt29CpUyeULFlSbaXg8+fP46+//kKPHj0AAOXKlYOnpyfWrFmDyMhI1K5dGxcvXsSmTZvQunXrr04JzozOnTtj7NixaNOmDYYOHYqPHz9i1apVKFasmNqg2BkzZuD06dNo1qwZnJycEB4ejpUrV6JgwYKoWbPmV9v/7bff0KRJE1SrVg29evXCp0+f8Pvvv8Pc3BzTpk3T2Ov4L5lMhkmTJn33uObNm2PGjBnw8vJC9erVcfPmTWzduhUuLi5qx7m6usLCwgI+Pj4wMzODiYkJqlSpgsKFC2corhMnTmDlypWYOnWqahr5xo0bUadOHUyePBnz58/PUHtEuYqWZ1kR0RcePHgg9unTR3R2dhYNDQ1FMzMzsUaNGuLvv/8uxsXFqY5LTEwUp0+fLhYuXFg0MDAQHR0dxfHjx6sdI4rJ07abNWuW6jr/nS78tWnboiiKx44dE8uUKSMaGhqKxYsXF7ds2ZJq2nZAQIDYqlUr0cHBQTQ0NBQdHBzELl26iA8ePEh1jf9ObT5+/LhYo0YNMU+ePKJcLhdbtGgh3rlzR+2YlOv9d1r4xo0bRQDi06dPv/qeiqL6tO2v+dq07ZEjR4r29vZinjx5xBo1aohBQUFpTrfet2+fWKpUKVFfX1/tddauXVssXbp0mtf8sp3o6GjRyclJrFChgpiYmKh23IgRI0SZTCYGBQV98zUQ5WaCKGZgNB0RERFRDsQxNERERCR5TGiIiIhI8pjQEBERkeQxoSEiIiLJY0JDREREkseEhoiIiCSPC+tJkFKpxKtXr2BmZqbRJdeJiCh7iKKImJgYODg4qO7ormlxcXFISEjQSFuGhoYwNjbWSFs/ChMaCXr16hUcHR21HQYREWVRSEgIChYsqPF24+LiUNjJFKHhmrk5rJ2dHZ4+fZqjkxomNBJkZmYGALh32QFmpuw11HX/V7aatkOgbCQmJWo7BMoGSWIizuKQ6u+5piUkJCA0XIHnV5whN8va50R0jBJOFZ8hISGBCQ1pVko3k5mpLMv/o1LOpy8YaDsEykYie5FzD1Gzd2pPi6mZAFOzrF1DCWn8T8mEhoiISEcpRCUUWbzBkUJUaiaYH4xf74mIiEjyWKEhIiLSUUqIUCJrJZqsnp9dmNAQERHpKCWUyGqHUdZbyB7sciIiIiLJY4WGiIhIRylEEQoxa11GWT0/uzChISIi0lEcQ0NERESSp4QIRS5JaDiGhoiIiCSPFRoiIiIdxS4nIiIikrzcNCiYXU5EREQkeazQEBER6Sjl/7astiEFTGiIiIh0lEIDs5yyen52YZcTERERSR4rNERERDpKISZvWW1DCpjQEBER6SiOoSEiIiLJU0KAAkKW25ACjqEhIiIiyWOFhoiISEcpxeQtq21IARMaIiIiHaXQQJdTVs/PLuxyIiIiIsljhYaIiEhH5aYKDRMaIiIiHaUUBSjFLM5yyuL52YVdTkRERCR5rNAQERHpKHY5ERERkeQpIIMii50xCg3F8qOxy4mIiIgkjxUaIiIiHSVqYFCwKJFBwUxoiIiIdBTH0BAREZHkKUQZFGIWx9BI5NYHHENDREREkscKDRERkY5SQoAyi7ULJaRRomFCQ0REpKNy0xgadjkRERGR5LFCQ0REpKM0MyiYXU5ERESkRcljaLJ4c0p2ORERERFlD1ZoiIiIdJRSA/dy4iwnIiIi0qrcNIaGXU5EREQkeazQEBER6SglZFxYj4iIiKRNIQpQZPFu2Vk9P7swoSEiItJRCg0MClZIpELDMTREREQkeazQEBER6SilKIMyi7OclBKZ5cSEhoiISEexy4mIiIhIQlihISIi0lFKZH2WklIzofxwTGiIiIh0lGbWoZFGZ440oiQiIiL6BlZoiIiIdJRm7uUkjdoHExoiIiIdpYQAJbI6hkYaKwVLI+1Kh2nTpqF8+fLfPKZOnToYPnx4tsRDqa3bFIOq7q/hUDwEDsVDUK9FKI6d+AQAePdegVGT3sGt1ivkdw1ByZ9fYvTkd4iKTns42tt3ChSv+BJmBV4gMurzMfsOf0TLzuFwLvuv6hrHAz9ly+ujjOk+qS2OxW9V29bf+E31/LAVPeF7dxEORG7En/+uwrRd3nAsbq/FiCkrytYqiRn7xmJHiA/8FX+iequf1Z4fvWEg/BV/qm2zD0/QUrQkRVqr0AjCtzO+qVOnYtq0aRq95u7du2FgYPDNY549e4bChQvj2rVraSZI06dPx8OHD7FlyxYIgoA9e/agdevWGo1TVznY62H6eAu4FtaHKALb/opF554ROHfUDqIIvA5TYNZkC5QoZoCQfxUYNu4dXoe+xZa1+VO1NWjUO5QpZYBXoQq1/ef/iUfdX4wxdZw5zOUybNkZi449InDyoB3KlTHMrpdK6fTsdgjGNpmjeqxI+vzzfHj1KU5sP4/wkDcwszRF98ltMefgOHgUHw6lUhrrYtBnxiZGeHL9GY5uPIFpf49O85iLftewoOdK1ePE+KTsCk9nscspG7x+/Vr17507d2LKlCm4f/++ap+pqanGr5kvX75vPp+QkPDdNvbt24dx48ZpKqRcpWnDvGqPp46zwPrNH3DxagI8u5hi6xeJi4uzAaaOtUDvoW+QlCRCX/9zArxuUwyiopUYN9wcx07EqbU5b4al2uNp4y1w6NgnHPH/xIQmB1IkKfE+LCrN5w6vP6n6d9jzN/Cd+hdWX5kLW+f8eP0kPLtCJA255BeMS37B3zwmMT7pq/8/UOZoZmE9aSQ0WovSzs5OtZmbm0MQBLV9aSU0gYGBqFy5MkxMTGBhYYEaNWrg+fPnasds3rwZzs7OMDc3R+fOnRETE6N67r9dTs7Ozpg5cyY8PDwgl8vRt29fFC5cGADg5uYGQRBQp04d1fEhISG4ffs2GjduDGdnZwBAmzZtIAiC6jEArFq1Cq6urjA0NETx4sWxefNmtRgFQcCqVavQpEkT5MmTBy4uLti1a1cm30lpUihE7NoXi9iPSlSpaJTmMVExSpiZytSSmXsPEjF3SRTWLLWCLB3/9yqVIj58UMLSQhq/kLlNgSK22P50OTbdW4xxvgOR39EqzeOM8xqhkWdtvH4ajoiQt9kcJWWXcrVL4c/Xa7HhzhIMXdEbZvk0/8U2t1GKgkY2KZDMX/mkpCS0bt0atWvXxo0bNxAUFIS+ffuqdV09fvwYe/fuxcGDB3Hw4EGcOnUKc+fO/Wa7CxYsQLly5XDt2jVMnjwZFy9eBAAcP34cr1+/xu7du1XH7t+/H3Xq1IFcLselS5cAABs3bsTr169Vj/fs2YNhw4Zh5MiRuHXrFvr16wcvLy+cPHlS7bqTJ09Gu3btcP36dXTr1g2dO3fG3bt3NfJe5WS37ybArmgIrAqHYPi4d9i2Lj9KFEvdDfjmnQLzl0TBq9vnP2jx8SK8Br7Br5Ms4VggfcXFpT4xiP0oom2LvN8/mLLVvUuP8Vvv1ZjQYh6WDdkAW+f8WBQwBXlMjVXHtOjnjn1v12P/+w34uVE5jGs6B0mJim+0SlJ16Wgw5vdYjjENZmDd+K346ZdSmH1oAmQyaXyYkvZJZpZTdHQ0oqKi0Lx5c7i6ugIASpYsqXaMUqmEr68vzMzMAADdu3dHQEAAZs2a9dV269Wrh5EjR6oe6+npAQCsrKxgZ2enduy+ffvQqlUrAED+/MndIxYWFmrHLViwAD169MDAgQMBAN7e3vjnn3+wYMEC1K1bV3Vchw4d0Lt3bwDAzJkz4e/vj99//x0rV37uP04RHx+P+Ph4tfdCqoq6GuDcMTtEx4jYe+gj+g1/C7+/bdWSmugYJTp4RKBEMQNMGGmu2j91TiSKFzVA53Ym6brWn3tiMXdRFHZsyI/81noafy2UNZeOXlf9++mtENy7+BhbHi5F7fZV4Od7CgAQsP0crgTchJWdJdqPaIpJW4dieJ3pSIxP1FbY9IME7jyv+vezWyF4cuM5Nj9ajnJ1SuPaiVtajEzalBrocuLCelnw4sULmJqaqrbZs2cjX7586NGjBxo1aoQWLVpg6dKlauNwgOQupJRkBgDs7e0RHv7tvvZKlSqlK6bo6GicOnUKLVu2/OZxd+/eRY0aNdT21ahRI1X1pVq1aqkef61CM2fOHJibm6s2R0fHdMWcExkaCnAtbAC3nwwxfbwFypYywMp1n7sFYz4o0aZbOExNBGxblx8GBp+/nZ0+F4c9Bz/CotALWBR6geadkn+2zmX/xawFkWrX2bUvFoNHvcMmH2vU/cUYlPPFRn3Evw9fw8H18xeEj9Gf8OpRGG6evYeZnZfCsbg9arRK3+8sSVvo03BERkTDoYjd9w+mr0q523ZWNynIkVE6ODggODhYtfXv3x9AcvdOUFAQqlevjp07d6JYsWL4559/VOf9dwaTIAhQKr99FwoTk/R92z9y5AhKlSqllWRi/PjxiIqKUm0hISHZHsOPolQC8QnJM1aiY5Ro1SUchoYCdvrmh7Gxeql5y1prBPnb4fyx5G35guRB3kd326Jvj8+J7F97YzHA+x02rLRCY/c82fdiKEuMTYxg72KLd6GRaT4vCAIgCDAw+vZMRdIN1gXyQW5linev32s7FJKIHNnlpK+vjyJFiqT5nJubG9zc3DB+/HhUq1YN27ZtQ9WqVTV2bUPD5JkwCoV6P/2X3U0pDAwMUh1XsmRJnDt3Dp6enqp9586dQ6lSpdSO++eff+Dh4aH22M3NLc2YjIyMYGSU9sBZKZk6JxIN6hrDsYA+PnxQ4s+9H3EmKB57t8lVycynOBHrfrdCTIyImJjk99baSgY9PQEuzuofZG/fJSerxYsawMI8OTf/c08s+g1/i/nTLfGzmxHCwpPbMDYWYC7Pkfl7rtVnblf8c+gqwl+8gZW9JTymtINSocTJnedhVzg/6rSvhivHbyDyTQzyF8iHTqNbIOFTwndnylDOZGxihAJfVFvsnG3gWs4J0e8+IObdB3Sf0gFnd1/Au9BIOLjaovfc/8OrR6G4/EXXJGWcAgIUWVwYL6vnZ5ccmdCk5enTp1izZg1atmwJBwcH3L9/Hw8fPlRLCjTBxsYGefLkgZ+fHwoWLAhjY2OYmJjgyJEjGDVqlNqxzs7OCAgIQI0aNWBkZARLS0uMHj0aHTt2hJubG9zd3XHgwAHs3r0bx48fVzv3r7/+QqVKlVCzZk1s3boVFy9exPr16zX6WnKaiDcK9Bv2FqHhCsjNZChT0gB7t+VHvV/y4Mz5OFy+ljxtvlwN9a7EW/84wMkxff+rbtz6AUlJgPfE9/Ce+PmbXdcOJli9JO0ZNKQd+Qvkw4Q/BsPMyhRRETG4ff4+hv0yFVFvYqBnoIcyNYujzZDGMLU0QWRYFG6evYfhdaYjMkK6Y8hys2KVXLHwxDTV4wGLkr/0HdsUiKUD18Llp0Jo4FEbphYmePvqHa7434DvlJ1ITOBaNFmhiS4jqXQ5SSahyZs3L+7du4dNmzbh7du3sLe3x6BBg9CvXz+NXkdfXx/Lli3DjBkzMGXKFNSqVQuTJ0+GqakpKlSooHbswoUL4e3tjbVr16JAgQJ49uwZWrdujaVLl2LBggUYNmwYChcujI0bN6pN/waSF+jbsWMHBg4cCHt7e2zfvj1VFUfXrFz49YSiVnVjxLwslKH20jrnyC7bTMVG2W929+Vffe7d60hMavXbV58n6blx6g4a6HX86vPjm8zOxmhIFwmiKHLJze8YOnQokpKS0pyBlBlZXWE4Ojoa5ubmeHmvIORm0sicKfPaFq6p7RAoG4lJnMGVGySJiQgU9yIqKgpyuVzj7ad8Tky54A5j06yNO4v7kIgZVY7/sFg1RTIVGm0qU6ZMqllJREREOV1u6nKSRpRa1rdvX5QtW1bbYRAREUnCihUr4OzsDGNjY1SpUkW1aO3XLFmyBMWLF0eePHng6OiIESNGIC4u7pvn/BcrNFrAXj4iIsoO2rg55c6dO+Ht7Q0fHx9UqVIFS5YsQaNGjXD//n3Y2NikOn7btm0YN24cNmzYgOrVq+PBgwfo0aMHBEHAokWL0n1dVmiIiIh0lAgByixuYganbS9atAh9+vSBl5cXSpUqBR8fH+TNmxcbNmxI8/jz58+jRo0a6Nq1K5ydndGwYUN06dLlu1Wd/2JCQ0REpKNSKjRZ3dIrISEBV65cgbu7u2qfTCaDu7s7goKC0jynevXquHLliiqBefLkCQ4fPoymTZtm6LWyy4mIiIi+67/3EUxr0dc3b95AoVDA1lZ9CQ1bW1vcu3cvzXa7du2KN2/eoGbNmhBFEUlJSejfvz8mTJiQofhYoSEiItJRSlHQyAYAjo6OavcVnDNnjkZiDAwMxOzZs7Fy5UpcvXoVu3fvxqFDhzBz5swMtcMKDRERkY5SaOBu2ynnh4SEqK1Dk9YteaytraGnp4ewsDC1/WFhYbCzS/tGo5MnT0b37t3Ru3dvAEDZsmURGxuLvn37YuLEiZDJ0hc/KzRERET0XXK5XG1LK6ExNDRExYoVERAQoNqnVCoREBDw1fXcPn78mCpp0dPTA5CxWcGs0BAREemoL7uMstJGRnh7e8PT0xOVKlVC5cqVsWTJEsTGxsLLywsA4OHhgQIFCqi6rFq0aIFFixbBzc0NVapUwaNHjzB58mS0aNFCldikBxMaIiIiHaWEDMosdsZk9PxOnTohIiICU6ZMQWhoKMqXLw8/Pz/VQOEXL16oVWQmTZoEQRAwadIkvHz5Evnz50eLFi0wa9asDF2X93KSIN7LKXfhvZxyF97LKXfIrns5DT7bBkZZvJdT/IdELK+5h/dyIiIiIu1QiAIUWexyyur52YUJDRERkY7SxhgabWF/BREREUkeKzREREQ6ShRlUGbx5pRiFs/PLkxoiIiIdJQCAhQZvLlkWm1IARMaIiIiHaUUsz4GRimRudDSqCMRERERfQMrNERERDpKqYExNFk9P7swoSEiItJRSghQZnEMTFbPzy7SSLuIiIiIvoEVGiIiIh3FlYKJiIhI8nLTGBppRElERET0DazQEBER6SglNHAvJ4kMCmZCQ0REpKNEDcxyEiWS0LDLiYiIiCSPFRoiIiIdpRQ10OXEWU5ERESkTblplhMTGiIiIh2Vmyo00ki7iIiIiL6BFRoiIiIdlZvu5cSEhoiISEexy4mIiIhIQlihISIi0lG5qULDhIaIiEhH5aaEhl1OREREJHms0BAREemo3FShYUJDRESko0Rkfdq1qJlQfjh2OREREZHksUJDRESko9jlRERERJLHhIaIiIgkLzclNBxDQ0RERJLHCg0REZGOyk0VGiY0REREOkoUBYhZTEiyen52YZcTERERSR4rNERERDpKCSHLC+tl9fzswoSGiIhIR+WmMTTsciIiIiLJY4WGiIhIR+WmQcFMaIiIiHQUu5yIiIiIJIQVGiIiIh3FLieShO7lq0NfMNR2GPSD7XoaqO0QKBu1K1hV2yFQdhDFbLpM1rucmNAQERGRVonIeu6UPalX1nEMDREREUkeKzREREQ6SgkBAlcKJiIiIinLTYOC2eVEREREkscKDRERkY5SigKEXLKwHhMaIiIiHSWKGpjlJJFpTuxyIiIiIsljhYaIiEhH5aZBwUxoiIiIdFRuSmjY5URERESSxwoNERGRjuIsJyIiIpK83DTLiQkNERGRjkpOaLI6hkZDwfxgHENDREREkscKDRERkY7KTbOcmNAQERHpKPF/W1bbkAJ2OREREZHksUJDRESko3JTlxMrNERERLpK1NCWQStWrICzszOMjY1RpUoVXLx48ZvHR0ZGYtCgQbC3t4eRkRGKFSuGw4cPZ+iarNAQERGRxuzcuRPe3t7w8fFBlSpVsGTJEjRq1Aj379+HjY1NquMTEhLQoEED2NjYYNeuXShQoACeP38OCwuLDF2XCQ0REZGu0kCXEzJ4/qJFi9CnTx94eXkBAHx8fHDo0CFs2LAB48aNS3X8hg0b8O7dO5w/fx4GBgYAAGdn5wyHyS4nIiIiHZWyUnBWt/RKSEjAlStX4O7urtonk8ng7u6OoKCgNM/Zv38/qlWrhkGDBsHW1hZlypTB7NmzoVAoMvRaWaEhIiKi74qOjlZ7bGRkBCMjI7V9b968gUKhgK2trdp+W1tb3Lt3L812nzx5ghMnTqBbt244fPgwHj16hIEDByIxMRFTp05Nd3ys0BAREemolFlOWd0AwNHREebm5qptzpw5GolRqVTCxsYGa9asQcWKFdGpUydMnDgRPj4+GWqHFRoiIiJdJQoZHgOTZhsAQkJCIJfLVbv/W50BAGtra+jp6SEsLExtf1hYGOzs7NJs3t7eHgYGBtDT01PtK1myJEJDQ5GQkABDQ8N0hckKDRERkY7S5BgauVyutqWV0BgaGqJixYoICAhQ7VMqlQgICEC1atXSjLFGjRp49OgRlEqlat+DBw9gb2+f7mQGYEJDREREGuTt7Y21a9di06ZNuHv3LgYMGIDY2FjVrCcPDw+MHz9edfyAAQPw7t07DBs2DA8ePMChQ4cwe/ZsDBo0KEPXZZcTERGRrtLCzZw6deqEiIgITJkyBaGhoShfvjz8/PxUA4VfvHgBmexzPcXR0RFHjx7FiBEj8NNPP6FAgQIYNmwYxo4dm6HrMqEhIiLSUdq69cHgwYMxePDgNJ8LDAxMta9atWr4559/MnydL7HLiYiIiCSPFRoiIiJdltUuJ4lgQkNERKSjeLdtIiIiIglhhYaIiEhXaWGWk7YwoSEiItJZwv+2rLaR87HLiYiIiCSPFRoiIiJdxS4nIiIikjwmNERERCR5Grzbdk7HMTREREQkeazQEBER6ShRTN6y2oYUaKxCExkZqammiIiISBNEDW0SkKmEZt68edi5c6fqcceOHWFlZYUCBQrg+vXrGguOiIiIKD0yldD4+PjA0dERAODv7w9/f38cOXIETZo0wejRozUaIBEREWVSyqDgrG4SkKkxNKGhoaqE5uDBg+jYsSMaNmwIZ2dnVKlSRaMBEhERUeYIYvKW1TakIFMVGktLS4SEhAAA/Pz84O7uDgAQRREKhUJz0RERERGlQ6YqNG3btkXXrl1RtGhRvH37Fk2aNAEAXLt2DUWKFNFogERERJRJXFjv2xYvXgxnZ2eEhIRg/vz5MDU1BQC8fv0aAwcO1GiARERElEm5aGG9TCU0BgYGGDVqVKr9I0aMyHJARERERBmV7oRm//796W60ZcuWmQqGiIiINIhdTqm1bt06XccJgsCBwURERDkBE5rUlErlj4yDiIiINC0XJTRZvvVBXFycJuIgIiIiyrRMJTQKhQIzZ85EgQIFYGpqiidPngAAJk+ejPXr12s0QCIiIsqkXLRScKYSmlmzZsHX1xfz58+HoaGhan+ZMmWwbt06jQVHuY9MJsBjSjtsurMI+9+ux8ZbC9B1XCu1Y2q0qoTZ+8fgr5CVOPpxM1x+KqSlaOlr1m36gOruYShY/CUKFn8J9xbh8D/xCQDw7r0Soye9R8VaobB1/Relf36NMZMjERWt3q1tXuDfVNuufR9Vz+8//AmtOkfApewr1TWOB7JiLCUtBzbC5icrcOjjViwLmo3iP3MdM01LWSk4q5sUZCqh+eOPP7BmzRp069YNenp6qv3lypXDvXv3NBYc5T4dRzZH8971scJ7E/q4jcX6STvRYUQztBrQUHWMcV4j3A56gPWTd36jJdKmAvZ6mDZejlNHbBB42Aa/1DBCl55vcfd+IkLDFHgdpsSvk80RFGCHlYstcfxkHAaPfJ+qnZWLLPHgmr1qa94oj+q58//Eo+4vxvhrszVOHbFBrepG6NzjDa7fSsjOl0qZVLtjdfRb6IktM/7CgIpj8eTGc8zxmwiL/HJth0YSlal1aF6+fJnmisBKpRKJiYlZDiozBOHbJbGpU6di2rRp2RMMZVqpqkURdOgqLvol37U97MUb1O1YDcUruaiOCdh+DgBgW8haKzHS9zVpmEft8ZRx5li/+QMuXU2ARxcTbFlrpXrOxVkfk8fK0XfoOyQlidDX//y7bG4ug62NHtIyd4aF2uOp481x+Ngn+PnHoVwZwzTPoZyj3YjmOLIuAEd9AwEAS/uvQZWmFdCoZz3snLdXq7HpFA4K/rZSpUrhzJkzqfbv2rULbm5uWQ4qM16/fq3alixZArlcrrbvy4UARVFEUlKSVuL8noSE3P3t8s4/D1G+TikUKGIHAHApWwilqxXDpWM3tBwZZZZCIWLXvo/4+FFE5YppJxrRMSLMTGVqyQwAjJr4HoXLvELdZmHYvCMWovj1v6xKpYgPH0RYWmR5rgP9YPoG+ihW0QVXj3/+vRZFEVeP30CpqsW0GBlJWaZ+86dMmYLBgwdj3rx5UCqV2L17N/r06YNZs2ZhypQpmo4xXezs7FSbubk5BEFQPb537x7MzMxw5MgRVKxYEUZGRjh79izi4+MxdOhQ2NjYwNjYGDVr1sSlS5dUbfr6+sLCwkLtOnv37lWrBl2/fh1169aFmZkZ5HI5KlasiMuXL6ueP3v2LGrVqoU8efLA0dERQ4cORWxsrOp5Z2dnzJw5Ex4eHpDL5ejbt++Pe5MkYOeCgzj11z9YFzwPh6I2YkXQTOxZcRQnd57XdmiUQbfvJsKh6EvkL/wS3uPeY+s6K5QoZpDquLfvFPhtSTR6dDNR2z9xlBy+PlbYu8MaLZvmwcgJ77F6w4evXm+Zzwd8+KhEmxZ5vnoM5Qzm1mbQ09fD+7Aotf3vw6NgaWehnaBI8jKV0LRq1QoHDhzA8ePHYWJigilTpuDu3bs4cOAAGjRooOkYNWbcuHGYO3cu7t69i59++gljxozB33//jU2bNuHq1asoUqQIGjVqhHfv3qW7zW7duqFgwYK4dOkSrly5gnHjxsHAIPmP9uPHj9G4cWO0a9cON27cwM6dO3H27FkMHjxYrY0FCxagXLlyuHbtGiZPnpzqGvHx8YiOjlbbdNUv7aqgXufqmNtjFQZVn4wFfdag/bAmcO9WU9uhUQYVddXHmWO2CDhog54epug//D3uPVDvko6OUaKDxxsUL6aP8SPVx06MGSFH1Z+NUK6MIUYMkmPYADMsW5V2QvPXno+YtygavquskN867S4qotxIgAYGBWv7RaRTpsbQAECtWrXg7++vyVh+uBkzZqgSrtjYWKxatQq+vr6qu4WvXbsW/v7+WL9+PUaPHp2uNl+8eIHRo0ejRIkSAICiRYuqnpszZw66deuG4cOHq55btmwZateujVWrVsHY2BgAUK9ePYwcOfKr15gzZw6mT5+e4dcrRX1md8bOhQdxatc/AIBnt/+FTSFrdB7VAse3ntVydJQRhoYCXAsn/4lx+8kQV4MTsGrdByydbwkAiPmgRLtub2BqIsPWddYwMPj2n81KboaYvyQG8fEijIw+H7tr30cMGfUem1bnQ91fjH/cCyKNiXoTA0WSApa25mr7LW3M8T40UjtB6apcdHPKLHU2X758GZs3b8bmzZtx5coVTcX0w1SqVEn178ePHyMxMRE1atRQ7TMwMEDlypVx9+7ddLfp7e2N3r17w93dHXPnzsXjx49Vz12/fh2+vr4wNTVVbY0aNYJSqcTTp0/TjCst48ePR1RUlGoLCQlJd3xSY5THEKJSfZyEUqGEIJPGLxR9nVIJJCQk/2yjY5Ro0+UNDA2BHb5WMDb+/s/3xu1EWFgI6snM3o8Y5P0O61fmQyN3djVJRVJiEh5ceQK3+mVV+wRBgFv9srjzzwMtRkZSlqkKzb///osuXbrg3LlzqjEmkZGRqF69Onbs2IGCBQtqMkaNMTEx+f5BX5DJZKkGIf53Fte0adPQtWtXHDp0CEeOHMHUqVOxY8cOtGnTBh8+fEC/fv0wdOjQVG0XKvR57ZTvxWVkZAQjI6MMxS5V/xwORucxLREe8gbP77yEa3kntB3SGMf+OK06xszSBPkdrWBln/xN37GoPQDgfVhUqj550o5pc6LQoK4xChbQw4cPIv7a+xFng+Kxe5u1Kpn5FCdize9WiIkREROTfP83aysZ9PQEHDn2CeFvlPi5giGMjAScPB2HRb/HYEh/U9U1/trzEf2Hv8Pc6Rao5GaIsPDkNoyNBZjLOTA4p/t78UGM8R2EB5cf4/7FR2gzvBmMTYxwdONJbYemW3LRLKdMJTS9e/dGYmIi7t69i+LFiwMA7t+/Dy8vL/Tu3Rt+fn4aDfJHcHV1haGhIc6dOwcnJycAycnKpUuXVF1E+fPnR0xMDGJjY1VJR3BwcKq2ihUrhmLFimHEiBHo0qULNm7ciDZt2qBChQq4c+dOmlPcKW0rR/4BzyntMHhJD1jkl+Pt6/c4vOEkts7eozqmarMKGLXm8+DpCZuTxyRtnrUbW2btSdUmZb+INwr0H/YOoeEKyM1kKF3SALu3WaPeL8Y4cz4Ol68lz+ZzqxGqdt6Nf+zg5KgPAwMBa30/YMK0JIhi8tTuWVPN1QYO+279gKQkYNTESIyaGKna37VDXqxaki9bXidl3qk/z8Mivxye0zvB0s4Cj4OfYUKTWYgM55cSjWJC822nTp3C+fPnVckMABQvXhy///47atWqpbHgfiQTExMMGDAAo0ePRr58+VCoUCHMnz8fHz9+RK9evQAAVapUQd68eTFhwgQMHToUFy5cgK+vr6qNT58+YfTo0Wjfvj0KFy6Mf//9F5cuXUK7du0AAGPHjkXVqlUxePBg9O7dGyYmJrhz5w78/f2xfPlybbzsHO/Thzj4jNkKnzFbv3qM/5Yz8N+SetkAyjlWLPx6QlGrujGiXn67iute1xjudb89HubQLptMxUY5x74Vfti3Iud/AZYyTaz0q9MrBTs6Oqa5gJ5CoYCDg0OWg8ouc+fORbt27dC9e3dUqFABjx49wtGjR2FpmdyVkS9fPmzZsgWHDx9G2bJlsX37drXF+fT09PD27Vt4eHigWLFi6NixI5o0aaIawPvTTz/h1KlTePDgAWrVqgU3NzdMmTJFUu8RERGRFAjit1aq+op9+/Zh9uzZWLFihWpA6+XLlzFkyBCMHTsWrVu31nSc9IXo6GiYm5ujnnFH6AtcEVXX/fUoUNshUDZqV7CqtkOgbJAkJiIQ+xAVFQW5XPO3e0j5nHD+dRZkxlmb/aeMi8OzSRN/WKyaku4uJ0tLS7UF5WJjY1GlShXo6yc3kZSUBH19ffTs2ZMJDRERUU7AMTSpLVmy5AeGQURERJR56U5oPD09f2QcREREpGG5aVBwplcKThEXF5fqhoo5uY+NiIgo1+BKwd8WGxuLwYMHw8bGBiYmJrC0tFTbiIiIiLJTphKaMWPG4MSJE1i1ahWMjIywbt06TJ8+HQ4ODvjjjz80HSMRERFlhqihTQIy1eV04MAB/PHHH6hTpw68vLxQq1YtFClSBE5OTti6dSu6deum6TiJiIgog3LTGJpMVWjevXsHFxcXAMnjZd69ewcAqFmzJk6fPv2tU4mIiIg0LlMJjYuLi+pu0SVKlMCff/4JILlyY25u/q1TiYiIKLvkoi6nTCU0Xl5euH79OgBg3LhxWLFiBYyNjTFixAiMGTNGowESERFRJomfu50yu0klocnUGJoRI0ao/u3u7o579+7hypUrsLa2xpYtWzQWHBEREWVBLlopOFMVmv9ycnJC27ZtYW5ujvXr12uiSSIiIqJ0y/LCekRERJRD5aIKDRMaIiIiHcVp20REREQSkqEKTdu2bb/5fGRkZFZiISIiIsqUDCU031tjxtzcHB4eHlkKiIiIiDSEY2jStnHjxh8VBxEREVGmcVAwERGRjspNg4KZ0BAREekyiSQkWcVZTkRERCR5rNAQERHpKg4KJiIiIqnjGBoiIiKSvlxUoeEYGiIiIpI8JjREREQ6KqXLKatbRq1YsQLOzs4wNjZGlSpVcPHixXSdt2PHDgiCgNatW2f4mkxoiIiIdJWooS0Ddu7cCW9vb0ydOhVXr15FuXLl0KhRI4SHh3/zvGfPnmHUqFGoVatWxi74P0xoiIiISGMWLVqEPn36wMvLC6VKlYKPjw/y5s2LDRs2fPUchUKBbt26Yfr06XBxccnUdZnQEBER6apsrtAkJCTgypUrcHd3V+2TyWRwd3dHUFDQV8+bMWMGbGxs0KtXrwy8OHWc5URERKSjNDltOzo6Wm2/kZERjIyM1Pa9efMGCoUCtra2avttbW1x7969NNs/e/Ys1q9fj+Dg4CzFyQoNERERfZejoyPMzc1V25w5c7LcZkxMDLp37461a9fC2to6S22xQkNERKSrNLgOTUhICORyuWr3f6szAGBtbQ09PT2EhYWp7Q8LC4OdnV2q4x8/foxnz56hRYsWqn1KpRIAoK+vj/v378PV1TVdYbJCQ0REpKs0OIZGLperbWklNIaGhqhYsSICAgJU+5RKJQICAlCtWrVUx5coUQI3b95EcHCwamvZsiXq1q2L4OBgODo6pvulskJDREREGuPt7Q1PT09UqlQJlStXxpIlSxAbGwsvLy8AgIeHBwoUKIA5c+bA2NgYZcqUUTvfwsICAFLt/x4mNERERDpKG/dy6tSpEyIiIjBlyhSEhoaifPny8PPzUw0UfvHiBWQyzXcQMaEhIiLSVVq6l9PgwYMxePDgNJ8LDAz85rm+vr4ZvyCY0BAREems3HS3bQ4KJiIiIsljhYaIiEhXaanLSRuY0BAREemqXJTQsMuJiIiIJI8VGiIiIh0l/G/LahtSwISGiIhIV7HLiYiIiEg6WKEhIiLSUblpHRomNERERLqKXU5ERERE0sEKDRERkS6TSIUlq5jQEBER6SiOoSEiIiLp4xgaIiIiIulghYaIiEhHscuJiIiIpI9dTkRERETSwQqNhCnj4qEUlNoOg36wdgWrajsEykZHXwVrOwTKBtExSlgW+/HXYZcTERERSR+7nIiIiIikgxUaIiIiXZWLKjRMaIiIiHQUx9AQERGR9OWiCg3H0BAREZHksUJDRESkowRRhCBmrcSS1fOzCxMaIiIiXcUuJyIiIiLpYIWGiIhIR3GWExEREUkfu5yIiIiIpIMVGiIiIh3FLiciIiKSPnY5EREREUkHKzREREQ6il1OREREJH25qMuJCQ0REZEOk0qFJas4hoaIiIgkjxUaIiIiXSWKyVtW25AAJjREREQ6KjcNCmaXExEREUkeKzRERES6irOciIiISOoEZfKW1TakgF1OREREJHms0BAREekqdjkRERGR1HGWExEREZGEsEJDRESkq7iwHhEREUldbupyYkJDRESkq3LRoGCOoSEiIiLJY4WGiIhIR7HLiYiIiKQvFw0KZpcTERERSR4rNERERDqKXU5EREQkfZzlRERERCQdrNAQERHpKHY5ERERkfQpxeQtq21IALuciIiISPJYoSEiItJVuWhQMBMaIiIiHSVAA2NoNBLJj8eEhoiISFdxpWAiIiKizFmxYgWcnZ1hbGyMKlWq4OLFi189du3atahVqxYsLS1haWkJd3f3bx7/NUxoiIiIdFTKtO2sbhmxc+dOeHt7Y+rUqbh69SrKlSuHRo0aITw8PM3jAwMD0aVLF5w8eRJBQUFwdHREw4YN8fLlywxdlwkNERGRrhI1tGXAokWL0KdPH3h5eaFUqVLw8fFB3rx5sWHDhjSP37p1KwYOHIjy5cujRIkSWLduHZRKJQICAjJ0XSY0REREpBEJCQm4cuUK3N3dVftkMhnc3d0RFBSUrjY+fvyIxMRE5MuXL0PX5qBgIiIiHSWIIoQsDupNOT86Olptv5GREYyMjNT2vXnzBgqFAra2tmr7bW1tce/evXRdb+zYsXBwcFBLitKDFRoiIiJdpdTQBsDR0RHm5uaqbc6cORoPd+7cudixYwf27NkDY2PjDJ3LCg0RERF9V0hICORyuerxf6szAGBtbQ09PT2EhYWp7Q8LC4Odnd0321+wYAHmzp2L48eP46effspwfKzQEBER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\n"
          },
          "metadata": {}
        }
      ]
    }
  ]
}
